package com.dd.cloud.user.service.order.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.cloud.common.constants.*;
import com.dd.cloud.common.enums.ZkExceptionEnum;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.interceptor.UserInterceptor;
import com.dd.cloud.common.utils.*;
import com.dd.cloud.common.vo.LoginInfo;
import com.dd.cloud.common.vo.PageVo;
import com.dd.cloud.user.config.OrderPrefix;
import com.dd.cloud.user.entity.consult.ConsultSubscribe;
import com.dd.cloud.user.entity.order.OrderPay;
import com.dd.cloud.user.entity.phar.Pharmacy;
import com.dd.cloud.user.entity.pres.PresPrescription;
import com.dd.cloud.user.entity.product.PayParam;
import com.dd.cloud.user.enums.ExceptionEnum;
import com.dd.cloud.user.mapper.order.OrderPayMapper;
import com.dd.cloud.user.mq.MQConfig;
import com.dd.cloud.user.mq.RabbitSend;
import com.dd.cloud.user.req.consult.ConsultNotPayReq;
import com.dd.cloud.user.req.order.*;
import com.dd.cloud.user.res.order.*;
import com.dd.cloud.user.service.admin.IAdminUserService;
import com.dd.cloud.user.service.consule.IConsultSubscribeService;
import com.dd.cloud.user.service.doctor.IPatientMedCardService;
import com.dd.cloud.user.service.order.IOrderFlowService;
import com.dd.cloud.user.service.order.IOrderPayService;
import com.dd.cloud.user.service.phar.IPharmacyService;
import com.dd.cloud.user.service.pres.IDrugsModelService;
import com.dd.cloud.user.service.pres.IPresPrescriptionService;
import com.dd.cloud.user.service.product.IPayParamService;
import com.dd.cloud.user.vo.DelayMqVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author czg
 * @since 2022-09-03
 */
@Slf4j
@Service
public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OrderPay> implements IOrderPayService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private OrderPrefix orderPrefix;
    @Autowired
    private IOrderFlowService orderFlowService;
    @Autowired
    private IPayParamService payParamService;
    @Autowired
    private RabbitSend rabbitSend;
    @Autowired
    private IPresPrescriptionService presPrescriptionService;
    @Autowired
    private IAdminUserService adminUserService;
    @Autowired
    private IConsultSubscribeService consultSubscribeService;
    @Autowired
    private IPatientMedCardService patientMedCardService;

    /**
     * 订单退款
     *
     * @param orderNo
     * @param delBo
     * @return
     */
    @Override
    public void refund(String orderNo, int delBo) {
        OrderPay orderPay = getById(orderNo);
        if (orderPay == null) {
            throw new ZekeException(ZkExceptionEnum.ORDER_NOT_FOUND);
        }
        if (orderPay.getStatus() != OrderConstants.Status.PAY) {
            throw new ZekeException(ZkExceptionEnum.ORDER_CAN_NOT_REFUND);
        }
        orderPay.setStatus(OrderConstants.Status.REFUND);
        if (delBo == 1) {
            orderPay.setStatus(OrderConstants.Status.DEL);
        }
        boolean flag = updateById(orderPay);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.REFUND_ERROR, "修改订单失败");
        }
        orderFlowService.refund(orderPay);
    }

    /**
     * 删除订单
     *
     * @param tencode
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delOrder(String tencode) {
        QueryWrapper<OrderPay> qw = new QueryWrapper<>();
        qw.lambda().eq(OrderPay::getServerNo, tencode);
        qw.lambda().eq(OrderPay::getStatus, OrderConstants.Status.NOPAY);
        OrderPay orderPay = getOne(qw);
        if (orderPay != null) {
            OrderPay update = new OrderPay();
            update.setOrderNo(orderPay.getOrderNo());
            update.setStatus(OrderConstants.Status.DEL);
            update.setUpdateDate(LocalDateTime.now());
            updateById(update);
        }
    }

    /**
     * 创建订单
     *
     * @param request
     * @param req
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderResponse<?> createOrder(HttpServletRequest request, CreateOrderReq req, Integer sourceType) {
        String orderNo;
        //线上问诊医生及智能处方编号
        if (sourceType != null && (sourceType == 3 || sourceType == 4)) {
            orderNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getPrePayPrefix());
        } else if (sourceType != null && sourceType == 5) {//问诊订单编号
            orderNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getWzOrderPayPrefix());
        } else if (sourceType != null && sourceType == 6) {//包月服务订单编号
            orderNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getMonthPayPrefix());
        } else { //线下医生开方,智能处方编号
            orderNo = SerialNumberUtils.getInstance().generateSerialNumber(redisTemplate, orderPrefix.getPayPrefix());
        }
        OrderPay orderPay = new OrderPay();
        orderPay.setStatus(OrderConstants.Status.NOPAY);
        orderPay.setOrderNo(orderNo);
        orderPay.setTotalFee(req.getPrice());
        orderPay.setActualPay(req.getPrice());
        orderPay.setServerNo(req.getServerNo());
        orderPay.setProduct(req.getProduct());
        String payType = req.getPayType().contains("WX") ? "wx" : "zfb";
        if (!StringUtils.isBlank(req.getApply())) {
            payType = payType + "_" + req.getApply();
        }
        PayParam payParam = payParamService.getPayParam(req.getPartyId(), req.getPartyType(), payType);
        if (payParam == null) {
            throw new ZekeException(400, "查询支付参数失败");
        }
        orderPay.setPayParamId(payParam.getId());
        int flag = getBaseMapper().insert(orderPay);
        if (flag <= 0) {
            throw new ZekeException(ExceptionEnum.PRES_CREATE_ERROR, "订单创建失败");
        }
        DelayMqVo<String> delayMqVo = new DelayMqVo<>();
        delayMqVo.setT(orderPay.getOrderNo());
        delayMqVo.setType(DelayMqVo.DelayMqVoType.ORDER_CANCEL);
        //毫秒级 24小时
        long ms = 1000 * 60 * 60 * 24;
        rabbitSend.sendMessageDelay(MQConfig.DELAY_EXCHANGE, MQConfig.DELAY_KEY, delayMqVo, ms);
        CreateFlowReq flowReq = new CreateFlowReq();
        flowReq.setOrderNo(orderNo);
        flowReq.setOpenid(req.getOpenid());
        flowReq.setPayType(req.getPayType());
        flowReq.setPrice(req.getPrice());
        PayOrderResponse<?> res = orderFlowService.createFlow(request, flowReq, payParam);
        res.setOrderPay(orderPay);
        return res;
    }

    /**
     * 重新支付未支付订单
     *
     * @param req
     * @return
     */
    @Override
    public PayOrderResponse payUnPayOrder(HttpServletRequest request, PayUnOrderReq req) {
        OrderPay orderPay = getById(req.getOrderNo());
        if (orderPay == null) {
            throw new ZekeException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        if (orderPay.getStatus() == OrderConstants.Status.PAY) {
            throw new ZekeException(ExceptionEnum.ORDER_IS_PAY);
        }
        if (orderPay.getStatus() != OrderConstants.Status.NOPAY) {
            throw new ZekeException(400, "订单未在待付款状态");
        }
        if (req.getUpdateAddr() == 1) {
            if (req.getTotalFee() != null && req.getExpensesFee() != null) {
                PresPrescription presPrescription = presPrescriptionService.getById(orderPay.getServerNo());
                int price = Math.round((presPrescription.getDiscountMoney() == null ? presPrescription.getMoney() : presPrescription.getDiscountMoney()) + presPrescription.getConsultationFee() + presPrescription.getFryingFee() + req.getExpensesFee() + presPrescription.getServiceFee());
                if (price != req.getTotalFee()) {
                    throw new ZekeException(400, "订单金额异常");
                }
                orderPay.setTotalFee(req.getTotalFee());
                orderPay.setActualPay(req.getTotalFee());
                updateById(orderPay);
            }
            req.setTencode(orderPay.getServerNo());
            presPrescriptionService.updatePresPatientAddrInfo(req);
        }
        PayParam payParam = payParamService.getById(orderPay.getPayParamId());
        if (payParam == null) {
            throw new ZekeException("查询支付参数失败");
        }
        if (!payParam.getPayType().equals(req.getApply())) {
            //换了支付渠道 如公众号换成小程序
            payParam = payParamService.getPayParam(payParam.getPartyId(), payParam.getPartyType(), req.getApply());
            if (payParam == null) {
                throw new ZekeException("查询支付参数失败");
            }
            OrderPay updatePayParam = new OrderPay();
            updatePayParam.setOrderNo(req.getOrderNo());
            updatePayParam.setPayParamId(payParam.getId());
            updateById(updatePayParam);
        }
        CreateFlowReq flowReq = new CreateFlowReq();
        flowReq.setOrderNo(orderPay.getOrderNo());
        flowReq.setOpenid(req.getOpenId());
        flowReq.setPayType(req.getPayType());
        flowReq.setPrice(orderPay.getTotalFee());
        PayOrderResponse payOrderResponse = orderFlowService.payUnpayFlow(request, flowReq, payParam);
        payOrderResponse.setOrderPay(orderPay);
        return payOrderResponse;
    }

    /**
     * 查询订单编号通过服务编号
     *
     * @param serverNo
     * @return
     */
    @Override
    public String queryOrderNoByServerNo(String serverNo) {
        QueryWrapper<OrderPay> qw = new QueryWrapper<>();
        qw.lambda().eq(OrderPay::getServerNo, serverNo);
        qw.lambda().select(OrderPay::getOrderNo);
        OrderPay orderPay = getOne(qw);
        return orderPay != null ? orderPay.getOrderNo() : null;
    }

    /**
     * 查询处方订单列表
     *
     * @param req
     * @return
     */
    @Override
    public IPage<OrderPresPageRes> queryOrderPresPage(OrderPresPageReq req) {
        if (req.getDoctorId() == null && req.getMedId() == null) {
            LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.DOCTOR).get();
            req.setDoctorId(loginInfo.getId());
        }
        Assert.verifyStatusIdsParam(req.getClass(), req);
        if (StringUtils.isNotBlank(req.getKeyword())) {
            req.setKeyword(req.getKeyword().toUpperCase());
        }
        IPage<OrderPresPageRes> iPage = req.pagePojo();
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus(PresConstants.PresStatus.PRES_STATUS_ALL);
        }
        iPage = getBaseMapper().queryOrderPresPage(iPage, req);
        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {
            List<OrderPresPageRes> list = iPage.getRecords();
            list.forEach(item -> {
                item.setStatusText(PresConstants.statusToStatusText(item.getOrderStatus(), item.getStatus()));
            });
        }
        return iPage;
    }

    /**
     * 查询处方订单列表 运营后台
     *
     * @param req
     * @return
     */
    @Override
    public IPage<OrderPresAdminPageRes> queryOrderPresAdminPage(OrderPresAdminPageReq req) {
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.ADMIN).get();
        Assert.verifyStatusIdsParam(req.getStatus(), "status", false);
        Assert.verifyStatusIdsParam(req.getOrderStatus(), "orderStatus", false);
        Assert.verifyStatusIdsParam(req.getMechanId(), "mechanId", false);
        Assert.verifyStatusIdsParam(req.getPharmacyId(), "pharmacyId", false);
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus("0,1,11,12,15,2,21,3,4,5,6,7,8,9");
        }
        if (StringUtils.isBlank(req.getOrderStatus())) {
            req.setOrderStatus("-2,-1,2,1,4");
        }
        Integer type = adminUserService.getAdminUserType(loginInfo.getId());
        if (type == AdminConstants.UserType.ZRK) {
            req.setIsAiPres(1);
        }
        IPage<OrderPresAdminPageRes> iPage = req.pagePojo();
        iPage = getBaseMapper().queryOrderPresAdminPage(iPage, req);
        for (OrderPresAdminPageRes item : iPage.getRecords()) {
            item.setStatusText(PresConstants.statusToStatusText(item.getOrderStatus(), item.getStatus()));
        }
        return iPage;
    }

    /**
     * 查询处方订单列表 运营后台 导出excel
     *
     * @param response
     * @param req
     */
    @Override
    public void exportOrderPresAdminExcel(HttpServletResponse response, OrderPresAdminPageReq req) {
        List<OrderPresAdminExcelRes> listRes = new ArrayList<>(500);
        req.setPage(1);
        req.setPageSize(100);
        IPage<OrderPresAdminPageRes> iPage;
        do {
            iPage = queryOrderPresAdminPage(req);
            for (OrderPresAdminPageRes item : iPage.getRecords()) {
                OrderPresAdminExcelRes res = new OrderPresAdminExcelRes();
                BeanUtils.copyProperties(item, res);
                res.setMoney(BigDecimalUtils.divide(item.getMoney(), 100));
                res.setExpensesFee(BigDecimalUtils.divide(item.getExpensesFee(), 100));
                res.setFryingFee(BigDecimalUtils.divide(item.getFryingFee(), 100));
                res.setConsultationFee(BigDecimalUtils.divide(item.getConsultationFee(), 100));
                listRes.add(res);
            }
            req.setPage(req.getPage() + 1);
        } while (iPage.getPages() > req.getPage());
        String title = "医生开方订单数据";
        if (req.getIsAiPres() == 1) {
            title = "智能开方订单数据";
        }
        try {
            ExtUtils.exportExcel(listRes, title, title, OrderPresAdminExcelRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询处方订单列表 药房后台
     *
     * @param req
     * @return
     */
    @Override
    public IPage<OrderPresPharmacyPageRes> queryOrderPresPharmacyPage(OrderPresPharmacyPageReq req) {
        if (req.getPharmacyId() == null) {
            LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.PHAR).get();
            req.setPharmacyId(loginInfo.getPharId());
        }
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        Assert.verifyStatusIdsParam(req.getStatus(), "status", false);
        Assert.verifyStatusIdsParam(req.getOrderStatus(), "orderStatus", false);
        Assert.verifyStatusIdsParam(req.getMechanId(), "mechanId", false);
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus("0,11,2,21,3,8,9");
        }
        if (StringUtils.isBlank(req.getOrderStatus())) {
            req.setOrderStatus("-2,-1,2");
        }
        IPage<OrderPresPharmacyPageRes> iPage = req.pagePojo();
        iPage = getBaseMapper().queryOrderPresPharmacyPage(iPage, req);
        return iPage;
    }

    /**
     * 查询处方交易明细列表 药房后台
     *
     * @param req
     * @return
     */
    @Override
    public PageVo<OrderPresDetailListRes> queryOrderPresDetailListPage(OrderPresDetailListReq req) {
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        if (req.getPharmacyId() == null) {
            LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.PHAR).get();
            req.setPharmacyId(loginInfo.getPharId());
        }
        Assert.verifyStatusIdsParam(req.getMechanId(), "mechanId", false);
        PageVo<OrderPresDetailListRes> pageVo = req.pageVoPojo();
        pageVo = getBaseMapper().queryOrderPresDetailListPage(pageVo, req);
        boolean isShow1 = true, isShow2 = true, isShow3 = true;
        if (!StringUtils.isBlank(req.getFeeType())) {
            if (!req.getFeeType().contains("1")) {
                isShow1 = false;
            }
            if (!req.getFeeType().contains("2")) {
                isShow2 = false;
            }
            if (!req.getFeeType().contains("3")) {
                isShow3 = false;
            }
        }
        for (OrderPresDetailListRes item : pageVo.getRecords()) {
            if (!isShow1) {
                item.setDrugsFee(0);
            }
            if (!isShow2) {
                item.setExpressFee(0);
            }
            if (!isShow3) {
                item.setFryingFee(0);
            }
            item.setFlowFee(item.getDrugsFee() + item.getFryingFee() + item.getExpressFee());
        }
        if (req.getIsStatistics() == 1) {
            OrderPresDetailStatisticsRes res = getBaseMapper().queryOrderPresDetailStatistics(req);
            if (res == null) {
                res = new OrderPresDetailStatisticsRes();
            }
            res.setTotalFee(res.getDrugsFee() + res.getFryingFee() + res.getExpressFee() + res.getConsultationFee());
            pageVo.setExtend(res);
        }
        return pageVo;
    }

    /**
     * 导出处方交易明细列表 药房后台
     *
     * @param req
     * @param response
     */
    @Override
    public void exportOrderPresDetailList(OrderPresDetailListReq req, HttpServletResponse response) {
        req.setIsStatistics(0);
        List<OrderPresDetailExcelRes> listRes = new ArrayList<>(500);
        req.setPage(1);
        req.setPageSize(100);
        int no = 1;
        PageVo<OrderPresDetailListRes> pageVo;
        do {
            pageVo = queryOrderPresDetailListPage(req);
            for (OrderPresDetailListRes item : pageVo.getRecords()) {
                OrderPresDetailExcelRes res = new OrderPresDetailExcelRes();
                BeanUtils.copyProperties(item, res);
                res.setNo(no);
                res.setDrugsFee(item.getFlowType() == 1 ? BigDecimalUtils.divide(item.getDrugsFee(), 100) : BigDecimalUtils.divide(item.getDrugsFee(), 100).negate());
                res.setExpressFee(item.getFlowType() == 1 ? BigDecimalUtils.divide(item.getExpressFee(), 100) : BigDecimalUtils.divide(item.getExpressFee(), 100).negate());
                res.setFryingFee(item.getFlowType() == 1 ? BigDecimalUtils.divide(item.getFryingFee(), 100) : BigDecimalUtils.divide(item.getFryingFee(), 100).negate());
                res.setFlowFee(item.getFlowType() == 1 ? BigDecimalUtils.divide(item.getFlowFee(), 100) : BigDecimalUtils.divide(item.getFlowFee(), 100).negate());
                res.setPresOriginalMoney(item.getFlowType() == 1 ? BigDecimalUtils.divide(item.getPresOriginalMoney(), 100) : BigDecimalUtils.divide(item.getPresOriginalMoney(), 100).negate());
                listRes.add(res);
                no++;
            }
            req.setPage(req.getPage() + 1);
        } while (pageVo.hasNext());
        String title = "交易明细报表";
        try {
            ExtUtils.exportExcel(listRes, title, title, OrderPresDetailExcelRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询处方收入统计列表 药房后台
     *
     * @param req
     * @return
     */
    @Override
    public PageVo<OrderPresIncomeStatisticsListRes> queryOrderPresIncomeStatisticsList(OrderPresIncomeStatisticsListReq req) {
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        if (req.getPharmacyId() == null) {
            LoginInfo loginInfo = UserInterceptor.getLoginUserInfo(LoginInfo.UserType.PHAR).get();
            req.setPharmacyId(loginInfo.getPharId());
        }
        req.setMonth(StringOperUtil.strAddApostrophe(req.getMonth(), ","));
        PageVo<OrderPresIncomeStatisticsListRes> pageVo = req.pageVoPojo();
        pageVo = getBaseMapper().queryOrderPresIncomeStatisticsList(pageVo, req);
        LocalDate start = req.getStart() != null ? req.getStart().toLocalDate() : null;
        LocalDate end = req.getEnd() != null ? req.getEnd().toLocalDate() : LocalDate.now();
        for (OrderPresIncomeStatisticsListRes item : pageVo.getRecords()) {
            LocalDateTime updateDate = item.getUpdateDate();
            LocalDateTime temp = DateUtils.getLastDayOfMonth(updateDate.toLocalDate());
            StringBuilder sb = new StringBuilder();
            if (end.getYear() == temp.getYear() && end.getMonthValue() == temp.getMonthValue() && temp.getDayOfMonth() >= end.getDayOfMonth()) {
                if (start != null && start.getYear() == updateDate.getYear() && start.getMonthValue() == updateDate.getMonthValue()) {
                    sb.append(start.getMonthValue()).append(".").append(start.getDayOfMonth()).append("-");
                } else {
                    sb.append(updateDate.getMonthValue()).append(".1-");
                }
                sb.append(end.getMonthValue()).append(".").append(end.getDayOfMonth());
            } else {
                if (start != null && start.getYear() == updateDate.getYear() && start.getMonthValue() == updateDate.getMonthValue()) {
                    sb.append(start.getMonthValue()).append(".").append(start.getDayOfMonth()).append("-");
                } else {
                    sb.append(updateDate.getMonthValue()).append(".1-");
                }
                sb.append(temp.getMonthValue()).append(".").append(temp.getDayOfMonth());
            }
            item.setStartEndDate(sb.toString());
            item.setTotalFee(item.getDrugsFee() + item.getFryingFee() + item.getExpressFee());
        }
        if (req.getIsStatistics() == 1) {
            OrderPresIncomeStatisticsRes res = getBaseMapper().queryOrderPresIncomeStatistics(req);
            if (res == null) {
                res = new OrderPresIncomeStatisticsRes();
            }
            res.setTotalFee(res.getDrugsFee() + res.getFryingFee() + res.getExpressFee() + res.getConsultationFee());
            pageVo.setExtend(res);
        }
        return pageVo;
    }

    /**
     * 导出处方收入统计列表 药房后台
     *
     * @param req
     * @param response
     */
    @Override
    public void exportOrderPresIncomeStatisticsList(OrderPresIncomeStatisticsListReq req, HttpServletResponse response) {
        req.setIsStatistics(0);
        List<OrderPresIncomeStatisticsExcelRes> listRes = new ArrayList<>(500);
        req.setPage(1);
        req.setPageSize(100);
        int no = 1;
        PageVo<OrderPresIncomeStatisticsListRes> pageVo;
        do {
            pageVo = queryOrderPresIncomeStatisticsList(req);
            for (OrderPresIncomeStatisticsListRes item : pageVo.getRecords()) {
                OrderPresIncomeStatisticsExcelRes res = new OrderPresIncomeStatisticsExcelRes();
                BeanUtils.copyProperties(item, res);
                res.setNo(no);
                res.setDrugsFee(BigDecimalUtils.divide(item.getDrugsFee(), 100));
                res.setExpressFee(BigDecimalUtils.divide(item.getExpressFee(), 100));
                res.setFryingFee(BigDecimalUtils.divide(item.getFryingFee(), 100));
                res.setTotalFee(BigDecimalUtils.divide(item.getTotalFee(), 100));
                listRes.add(res);
                no++;
            }
            req.setPage(req.getPage() + 1);
        } while (pageVo.hasNext());
        String title = "收入统计报表";
        try {
            ExtUtils.exportExcel(listRes, title, title, OrderPresIncomeStatisticsExcelRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询处方订单收益列表
     *
     * @param req
     * @return
     */
    @Override
    public PageVo<OrderProfitPageRes> queryOrderProfitPage(OrderProfitPageReq req) {
        req.verifyParam();
        PageVo<OrderProfitPageRes> pageVo = req.pageVoPojo();
        pageVo = getBaseMapper().queryOrderProfitPage(pageVo, req);
        if (req.getStatistics() == 1) {
            pageVo.setExtend(getBaseMapper().queryOrderProfitTotal(req));
        }
        return pageVo;
    }

    /**
     * 查询订单详情
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderPresDetailRes queryOrderPresDetail(String orderNo) {
        OrderPresDetailRes res = getBaseMapper().queryOrderPresDetail(orderNo);
        if (res != null) {
            res.setDrugList(getBaseMapper().queryOrderPresDrugList(orderNo));
            res.setFlowList(getBaseMapper().queryOrderFlowList(orderNo));
        }
        return res;
    }

    /**
     * 结算订单
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementOrder(OrderSettlementReq req) {
        String[] orderNos = req.getOrderNos().split(",");
        if (!"-1".equals(req.getOrderNos())) {
            req.setOrderNoArr(orderNos);
            req.setPharmacyId(null);
            req.setStart(null);
            req.setEnd(null);
        } else {
            Assert.verifyStatusIdsParam(req.getPharmacyId(), "pharmacyId", false);
            if (req.verify()) {
                throw new ZekeException(400, "日期参数错误");
            }
        }
        int update = getBaseMapper().settlementOrder(req);
        if (!"-1".equals(req.getOrderNos()) && update != orderNos.length) {
            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
        }
    }

    /**
     * 导出处方订单收益列表excel
     *
     * @param response
     * @param req
     * @return
     */
    @Override
    public void exportOrderProfitExcel(HttpServletResponse response, OrderProfitPageReq req) {
        req.setSettlement(OrderConstants.OrderSettlement.UN_SETTLEMENT + "");
        req.verifyParam();
        List<OrderProfitExcelRes> listRes = new ArrayList<>(500);
        req.setPage(1);
        req.setPageSize(100);
        int no = 1;
        PageVo<OrderProfitPageRes> pageVo;
        do {
            pageVo = req.pageVoPojo();
            pageVo = getBaseMapper().queryOrderProfitPage(pageVo, req);
            for (OrderProfitPageRes item : pageVo.getRecords()) {
                OrderProfitExcelRes res = new OrderProfitExcelRes();
                BeanUtils.copyProperties(item, res);
                res.setNo(no);
                res.setDrugFee(BigDecimalUtils.divide(item.getDrugFee(), 100));
                res.setExpressFee(BigDecimalUtils.divide(item.getExpressFee(), 100));
                res.setActualPay(BigDecimalUtils.divide(item.getActualPay(), 100));
                res.setSettlement("待结算");
                listRes.add(res);
                no++;
            }
            req.setPage(req.getPage() + 1);
        } while (pageVo.hasNext());
        String title = "";
        if (!StringUtils.isBlank(req.getPharmacyId())) {
            title += getBaseMapper().queryPharmacyNames(req.getPharmacyId()) + "药房";
        }
        if (req.getStart() != null) {
            title += DateUtils.DTF_YMD_ZH.format(req.getStart()) + "-" + DateUtils.DTF_YMD_ZH.format(req.getEnd());
        }
        title += "待结算列表";
        try {
            ExtUtils.exportExcel(listRes, title, title, OrderProfitExcelRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询机构统计列表
     *
     * @param req
     * @return
     */
    @Override
    public IPage<OrderStatisticsPageRes> queryOrderStatisticsPage(OrderStatisticsPageReq req) {
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        Assert.verifyStatusIdsParam(req.getClass(), req);
        IPage<OrderStatisticsPageRes> iPage = req.pagePojo();
        iPage = getBaseMapper().queryOrderStatisticsPage(iPage, req);
        return iPage;
    }

    /**
     * 导出机构统计列表excel
     *
     * @param response
     * @param req
     * @return
     */
    @Override
    public void exportOrderStatisticsExcel(HttpServletResponse response, OrderStatisticsPageReq req) {
        if (req.verify()) {
            throw new ZekeException(400, "日期参数错误");
        }
        int no = 1;
        req.setPage(1);
        req.setPageSize(100);
        IPage<OrderStatisticsPageRes> iPage;
        List<OrderStatisticsExcelRes> listRes = new ArrayList<>(100);
        do {
            iPage = req.pagePojo();
            iPage = getBaseMapper().queryOrderStatisticsPage(iPage, req);
            for (OrderStatisticsPageRes item : iPage.getRecords()) {
                OrderStatisticsExcelRes res = new OrderStatisticsExcelRes();
                BeanUtils.copyProperties(item, res);
                res.setNo(no);
                res.setDrugFee(BigDecimalUtils.divide(item.getDrugFee(), 100));
                res.setExpressFee(BigDecimalUtils.divide(item.getExpressFee(), 100));
                res.setActualPay(BigDecimalUtils.divide(item.getActualPay(), 100));
                switch (item.getStatus()) {
                    case PresConstants.PresStatus.NOMER:
                    case PresConstants.PresStatus.SENDPHARM:
                        res.setStatus("待审方");
                        if (item.getOrderStatus().equals(OrderConstants.Status.NOPAY)) {
                            res.setStatus("待付款");
                        }
                        break;
                    case PresConstants.PresStatus.AUDIT:
                        res.setStatus("审核");
                        break;
                    case PresConstants.PresStatus.ADJUST:
                        res.setStatus("调剂");
                        break;
                    case PresConstants.PresStatus.REVIEW:
                        res.setStatus("复核");
                        break;
                    case PresConstants.PresStatus.BMED:
                        res.setStatus("泡药");
                        break;
                    case PresConstants.PresStatus.TISANES:
                        res.setStatus("煎药");
                        break;
                    case PresConstants.PresStatus.PACK:
                        res.setStatus("包装");
                        break;
                    case PresConstants.PresStatus.DELIVERY:
                        res.setStatus("发货");
                        break;
                    case PresConstants.PresStatus.DEL:
                        res.setStatus("已退款");
                        if (item.getOrderStatus().equals(OrderConstants.Status.TIMEOUT)) {
                            res.setStatus("已失效");
                        }
                        break;
                    default:
                        res.setStatus("未知状态");
                }
                listRes.add(res);
                no++;
            }
            req.setPage(req.getPage() + 1);
        } while (iPage.getCurrent() < iPage.getPages());
        String title = "机构订单统计报表";
        try {
            ExtUtils.exportExcel(listRes, title, title, OrderProfitExcelRes.class, title, true, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 机构支付统计列表
     *
     * @param req
     * @return
     */
    @Override
    public PageVo<MechanPayStatisticsPageRes> queryMechanPayStatisticsPage(MechanPayStatisticsPageReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (req.getTapId() == null) {
            throw new ZekeException(400, "一级机构id不能为空");
        }
        Assert.verifyStatusIdsParam(req.getClass(), req);
        PageVo<MechanPayStatisticsPageRes> pageVo = req.pageVoPojo();
        pageVo = getBaseMapper().queryMechanPayStatisticsPage(pageVo, req);
        if (req.getStatistics() == 1) {
            pageVo.setExtend(getBaseMapper().queryMechanPayStatisticsTotalPrice(req));
        }
        return pageVo;
    }

    /**
     * 医生支付统计列表
     *
     * @param req
     * @return
     */
    @Override
    public PageVo<DoctorPayStatisticsPageRes> queryDoctorPayStatisticsPage(DoctorPayStatisticsPageReq req) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        if (req.getTapId() == null) {
            throw new ZekeException(400, "一级机构id不能为空");
        }
        Assert.verifyStatusIdsParam(req.getClass(), req);
        PageVo<DoctorPayStatisticsPageRes> pageVo = req.pageVoPojo();
        pageVo = getBaseMapper().queryDoctorPayStatisticsPage(pageVo, req);
        if (req.getStatistics() == 1) {
            pageVo.setExtend(getBaseMapper().queryDoctorPayStatisticsTotalPrice(req));
        }
        return pageVo;
    }

    /**
     * 查询订单情况
     *
     * @param orderNo
     * @return
     */
    @Override
    public OrderPay getOrderPay(String orderNo) {
        return getById(orderNo);
    }

    @Autowired
    private IDrugsModelService drugsModelService;
    @Autowired
    private IPharmacyService pharmacyService;

    /**
     * 取消订单
     *
     * @param orderNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(String orderNo, Integer type) {
        LoginInfo loginInfo = UserInterceptor.getLoginUserInfo().get();
        OrderPay orderPay = getById(orderNo);
        if (orderPay == null) {
            if (loginInfo == null) {
                return;
            }
            throw new ZekeException(400, "订单不存在");
        }
        if (orderPay.getStatus() != OrderConstants.Status.NOPAY) {
            if (loginInfo == null) {
                return;
            }
            throw new ZekeException(400, "订单未在待付款状态");
        }
        QueryWrapper<PresPrescription> qw = new QueryWrapper<>();
        qw.lambda().eq(PresPrescription::getTencode, orderPay.getServerNo());
        if (loginInfo != null && loginInfo.getType() == LoginInfo.UserType.DOCTOR) {
            qw.lambda().eq(PresPrescription::getDoctorId, loginInfo.getId());
        }
        PresPrescription presPrescription = presPrescriptionService.getOne(qw);
        if (ObjectUtil.isNull(presPrescription)) {
            if (loginInfo == null) {
                return;
            }
            throw new ZekeException(400, "处方信息不存在");
        }
        OrderPay update = new OrderPay();
        update.setOrderNo(orderNo);
        update.setStatus(OrderConstants.Status.CANCEL);
        update.setUpdateDate(LocalDateTime.now());
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(400, "取消订单失败");
        }
        //清除处方详情缓存
        redisTemplate.delete(RedisKeyConstants.PRES_DETAIL_REDIS_KEY + orderPay.getServerNo());
        Pharmacy pharmacy = pharmacyService.getById(presPrescription.getPharmId());
        if (pharmacy.getFlag() == PharConstants.Flag.STORE) {
            //解锁库存
            presPrescriptionService.releasePresDrugsStock(presPrescription, type);
        }
        UpdateWrapper<PresPrescription> uw = new UpdateWrapper<>();
        uw.lambda().eq(PresPrescription::getTencode, orderPay.getServerNo());
        uw.lambda().set(PresPrescription::getStatus, PresConstants.PresStatus.CANCEL);
        presPrescriptionService.update(uw);
        PresPrescription pres = presPrescriptionService.getById(orderPay.getServerNo());
        if (pres.getConsultId() != null) {
            consultSubscribeService.consultPresPayTimeout(pres.getConsultId(), pres.getTencode());
        }
    }

    /**
     * 查询订单流水列表
     *
     * @param tencode
     * @return
     */
    @Override
    public List<OrderFlowListRes> queryOrderFlowListByTencode(String tencode) {
        return getBaseMapper().queryOrderFlowListByTencode(tencode);
    }


    /**
     * 查询问诊处方
     *
     * @param req
     */
    @Override
    public IPage<InquiryPresRes> selectInquiryPres(InquiryPresReq req) {
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus("0,11,12,2,21,3,8,9");
        }
        if (StringUtils.isBlank(req.getOrderStatus())) {
            req.setOrderStatus("-2,-1,2");
        }
        IPage<InquiryPresRes> iPage = req.pagePojo();
        iPage = getBaseMapper().selectInquiryPres(iPage, req);
        return iPage;
    }

    /**
     * 查询问诊订单
     *
     * @param req
     */
    @Override
    public IPage<InquireOrderRes> selectInquiryOrder(InquiryOrderReq req) {
        if (StringUtils.isBlank(req.getStatus())) {
            req.setStatus(ConsultConstants.Status.ADMIN_CAN_QUE);
        }
        IPage<InquireOrderRes> iPage = req.pagePojo();
        iPage = getBaseMapper().selectInquiryOrder(iPage, req);
        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {
            for (InquireOrderRes res : iPage.getRecords()) {
                LocalDateTime createDate = res.getCreateDate();
                LocalDateTime startDate = res.getStartDate();
                if (createDate != null && startDate != null) {
                    Duration duration = Duration.between(createDate, startDate);
                    long minutes = duration.toMinutes();
                    res.setWaitTime(minutes);
                }
            }
        }
        return iPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundOrder(String orderNo) {
        OrderPay orderPay = getById(orderNo);
        if (orderPay == null) {
            throw new ZekeException(400, "订单不存在");
        }
        ConsultSubscribe consultSubscribe = consultSubscribeService.getById(orderPay.getServerNo());
        if (ObjectUtil.isNull(consultSubscribe)) {
            throw new ZekeException(400, "预约问诊信息不存在");
        }
        if (orderPay.getStatus() != OrderConstants.Status.PAY && consultSubscribe.getStatus() != ConsultConstants.Status.SUBMIT) {
            throw new ZekeException(400, "该订单不能取消");
        }
        OrderPay update = new OrderPay();
        update.setOrderNo(orderNo);
        update.setStatus(OrderConstants.Status.REFUND);
        update.setUpdateDate(LocalDateTime.now());
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(400, "订单修改状态失败");
        }
        orderFlowService.refund(orderPay);
        //退款埋点
        String wzOrderNo = "wz" + orderPay.getOrderNo();
        redisTemplate.opsForValue().set(wzOrderNo, "0", 5, TimeUnit.MINUTES);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderStatus(String orderNo) {
        OrderPay orderPay = getById(orderNo);
        if (orderPay == null) {
            throw new ZekeException(400, "订单不存在");
        }
        ConsultSubscribe consultSubscribe = consultSubscribeService.getById(orderPay.getServerNo());
        if (ObjectUtil.isNull(consultSubscribe)) {
            throw new ZekeException(400, "预约问诊信息不存在");
        }
        if (orderPay.getStatus() == OrderConstants.Status.TIMEOUT && consultSubscribe.getStatus() != ConsultConstants.Status.CANCEL) {
            throw new ZekeException(400, "该订单不能取消");
        }
        OrderPay update = new OrderPay();
        update.setOrderNo(orderNo);
        update.setStatus(OrderConstants.Status.DEL);
        update.setUpdateDate(LocalDateTime.now());
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(400, "删除订单失败");
        }
        UpdateWrapper<ConsultSubscribe> uw = new UpdateWrapper<>();
        uw.lambda().eq(ConsultSubscribe::getId, orderPay.getServerNo());
        uw.lambda().set(ConsultSubscribe::getStatus, ConsultConstants.Status.DEL);
        boolean flag2 = consultSubscribeService.update(uw);
        if (!flag2) {
            throw new ZekeException(400, "删除订单失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayOrderResponse<?> orderPayagain(HttpServletRequest request, ConsultNotPayReq req) {
        OrderPay orderPay = getById(req.getOrderNo());
        Integer conslutId = Integer.valueOf(orderPay.getServerNo());
        ConsultSubscribe consultSubscribe = consultSubscribeService.getById(conslutId);
        if (orderPay == null) {
            throw new ZekeException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        if (consultSubscribe == null) {
            throw new ZekeException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        if (orderPay.getStatus() == OrderConstants.Status.PAY) {
            throw new ZekeException(ExceptionEnum.ORDER_IS_PAY);
        }
        if (orderPay.getStatus() != OrderConstants.Status.NOPAY) {
            throw new ZekeException(400, "订单未在待付款状态");
        }
        if (consultSubscribe.getStatus() != ConsultConstants.Status.NOPAY) {
            throw new ZekeException(400, "问诊订单未在待付款状态");
        }
        if (!orderPay.getTotalFee().equals(req.getTotalMoney())) {
            throw new ZekeException(400, "订单金额异常");
        }
        PayParam payParam = payParamService.getById(orderPay.getPayParamId());
        if (payParam == null) {
            throw new ZekeException("查询支付参数失败");
        }
        orderPay.setActualPay(req.getTotalMoney()).setTotalFee(req.getTotalMoney());
        this.updateById(orderPay);
        CreateFlowReq flowReq = new CreateFlowReq();
        flowReq.setOrderNo(orderPay.getOrderNo());
        flowReq.setOpenid(req.getOpenId());
        flowReq.setPayType(req.getPayType());
        flowReq.setPrice(orderPay.getTotalFee());
        PayOrderResponse payOrderResponse = orderFlowService.payUnpayFlow(request, flowReq, payParam);
        payOrderResponse.setOrderPay(orderPay);
        payOrderResponse.setConsultId(consultSubscribe.getId());
        return payOrderResponse;
    }

    /**
     * 测试退款用
     */
    @Override
    public void testRefundOrder(String orderNo) {
        OrderPay orderPay = getById(orderNo);
        OrderPay update = new OrderPay();
        update.setOrderNo(orderNo);
        update.setStatus(OrderConstants.Status.REFUND);
        update.setUpdateDate(LocalDateTime.now());
        boolean flag = updateById(update);
        if (!flag) {
            throw new ZekeException(400, "订单修改状态失败");
        }
        orderFlowService.refund(orderPay);
    }

    @Override
    public String refundIsSuccess(String orderNo) {
        String flag = "wz" + orderNo;
        String res = redisTemplate.opsForValue().get(flag);
        return res;
    }
}
